#ifndef __LOG_HPP__
#define __LOG_HPP__

#include <iostream>
#include <string>
#include <sstream>
#include <filesystem>
#include <fstream>
#include "Mutex.hpp"
#include <unistd.h>
#include <memory>
#include <ctime>
//写这个代码出现的问题：
// 1.不细心，有些细节没注意
// 2.逻辑链路不完整，对有些函数或类的功能并不清楚
// 3.解决bug能力欠缺




// 刷新模式：文件或者显示器，也是共享资源，也需要加锁
// 涉及到资源的访问一定要看是否为共享资源，是就加锁
// c++17 filesystem文件操作
// 向文件中写入一定是追加模式

// 日志等级
// 获取时间戳

// 日志内嵌套一个内部类，目的是表示一条日志
namespace LogModule
{
    using namespace MutexModule;
    class LogStrategy
    {
    public:
        LogStrategy() {}
        ~LogStrategy() {}
        virtual void SyncStrategy(const std::string message) = 0;
    };

    class ConsoleSyncStrategy : public LogStrategy
    {
    public:
        ConsoleSyncStrategy() {}
        void SyncStrategy(const std::string message) override
        {
            // 这里应该写什么？还是不太明白
            MutexProtect mp(&_mutex);
            std::cout << message << std::endl;
        }
        ~ConsoleSyncStrategy() {}

    private:
        // 需要定义属性吗
        Mutex _mutex;
    };

    const std::string defaultpath = "./log";
    const std::string defaultfile = "mylog.log";

    class FileSyncStrategy : public LogStrategy
    {
    public:
        FileSyncStrategy(std::string path = defaultpath, std::string file = defaultfile)
            : _path(path),
              _file(file)
        {
            MutexProtect mp(&_mutex);
            // 文件可能并不存在，如果不存在先创建
            if (std::filesystem::exists(_path))
            {
                return;
            }
            // 文件可能会因为权限等问题创建失败，所以要做好失败处理
            try
            {
                std::filesystem::create_directory(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        void SyncStrategy(const std::string message) override
        {
            // 保护临界区资源
            MutexProtect mp(&_mutex);
            // 打开文件进行写入
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            //打开文件并追加写入
            std::ofstream out(filename, std::ios::app);
            // 查看文件是否为已打开的状态
            if (!out.is_open())
            {
                return;
            }
            out << message << std::endl;
            out.close();
        }
        ~FileSyncStrategy() {}

    private:
        std::string _path;
        std::string _file;
        Mutex _mutex;
    };

    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string GetTimeStamp()
    {
        time_t t = time(nullptr);
        struct tm curr_time;
        localtime_r(&t, &curr_time);
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 curr_time.tm_year + 1900,
                 curr_time.tm_mon + 1,
                 curr_time.tm_mday,
                 curr_time.tm_hour,
                 curr_time.tm_min,
                 curr_time.tm_sec);
        return buffer;
    }

    std::string LevelToString(LogLevel &level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }
    class Logger
    {

        // 表示一条日志消息
        // 需要重载()
    public:
        Logger()
        {
            _flushe_strategy = std::make_unique<ConsoleSyncStrategy>();
        }

        void EnableConsoleLogSync()
        {
            _flushe_strategy = std::make_unique<ConsoleSyncStrategy>();
        }

        void EnableFileSyncStrategy()
        {
            _flushe_strategy = std::make_unique<FileSyncStrategy>();
        }

        class LogMessger
        {
            // 需要重载 <<
        public:
            LogMessger(LogLevel &level, std::string src_name, int line_number, Logger &logger)
                : _curr_time(GetTimeStamp()),
                  _level(level),
                  _src_name(src_name),
                  _pid(getpid()),
                  _line_number(line_number),
                  _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _curr_time << "]"
                   << "[" << LevelToString(level) << "]"
                   << "[" << _pid << "]"
                   << "[" << _src_name << "]"
                   << "[" << _line_number << "]"
                   << "-";

                _log_info = ss.str();
            }

            // 处理连续输出的情况LogMessger()<<"da"<<"fafa"<<2.3;
            template <typename T>
            LogMessger &operator<<(const T &info)
            {
                std::stringstream ss;
                ss << info;
                _log_info += ss.str();
                return *this;
            }

            ~LogMessger() 
            {
                if(_logger._flushe_strategy)
                {
                    _logger._flushe_strategy->SyncStrategy(_log_info);
                }
            }

        private:
            std::string _curr_time;
            LogLevel _level;
            std::string _src_name;
            pid_t _pid;
            int _line_number;
            std::string _log_info;
            Logger &_logger;
        };

    public:

        LogMessger operator()(LogLevel level, std::string src_name, int line)
        {
            return LogMessger(level, src_name, line, *this);
        }

        ~Logger()
        {
        }

    private:
        std::unique_ptr<LogStrategy> _flushe_strategy;
    };

    Logger log;
    #define Enable_File_Log_Sync_Strategy log.EnableFileSyncStrategy()
    #define Enable_Console_Sync_Strategy log.EnableConsoleLogSync()
    #define LOG(level) log(LogLevel::level, __FILE__, __LINE__)

}

#endif